package cn.good.yan.config.filters;

import cn.good.yan.config.pg.WhiteListConfig;
import cn.good.yan.config.util.JwtTokenUtil;
import cn.good.yan.entity.UserLogin;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 自定义过滤器  - 全局过滤器配置 （全局过滤器）
 *
 * @author shengren.yan
 * @create 2024/11/23
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    // 开始调用方法的时间
    public static final String BEGIN_TIME = "begin_time";

    // 获取白名单列表
    @Autowired
    private WhiteListConfig whiteListConfig;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;


    /**
     * 所有通过网关请求的，会先进入到这里
     * // 跳过不需要验证的路径（白名单）
     * if (StringUtils.matches(url, ignoreWhite.getWhites())) {
     * return chain.filter(exchange);
     * }
     * // 验证令牌（空、过期、不正确）
     * // MultiValueMap<String, String> queryParams = request.getQueryParams();
     * // 获取请求参数，防止sql注入校验规则（就是字符串过滤）
     *
     * @param exchange
     * @param chain
     * @return {@link Mono }<{@link Void }>
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 记录日志信息
        log.info("执行全局网关过滤器【开始】: ------------ ");
        // 先记录下访问接口的开始时间
        exchange.getAttributes().put(BEGIN_TIME, System.currentTimeMillis());


        // 获得请求和响应对象
        ServerHttpRequest request = exchange.getRequest();
        // 对白名单中的地址放行 （白名单）
        List<String> whiteList = whiteListConfig.getWhiteList();
        for (String str : whiteList) {
            String path = request.getURI().getPath();  // 获取请求地址
            if (path.contains(str)) {
                log.info("白名单，放行{}", request.getURI().getPath());
                return chain.filter(exchange);
            }
        }

        // 获取请求参数 （uname）
//        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
//        log.info("访问接口主机：" + exchange.getRequest().getURI().getHost());
//        log.info("访问接口端口：" + exchange.getRequest().getURI().getPort());
//        log.info("访问接口URL：" + exchange.getRequest().getURI().getPath());
//        log.info("访问接口URL后面参数：" + exchange.getRequest().getURI().getRawQuery());
//        log.info("访问接口时长" + (System.currentTimeMillis() - beginVisitTime) + "毫秒");


        // 获取 token（消息头） 是否存在，并且开头以 tokenHead配置的为开头 (tokenHeader)在yml配置文件配置
        // 正常的请求头的  key是Authorization  value 是  Bearer jwt生产的token令牌
        String header = request.getHeaders().getFirst(tokenHeader);
        if (null != header && header.startsWith(tokenHead)) {
            String token = header.substring(tokenHead.length());
            // 从token中获取登录用户名
            JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
            String username = jwtTokenUtil.getUserFromToken(token);
            // 存在token，但用户名未登录
            if (null != username) {
                UserLogin userLogin = new UserLogin();
                userLogin.setUsername(username);
                // 验证token是否有效
                if (jwtTokenUtil.validateToken(token, userLogin)) {
                    // 当前用户存入
                    exchange.getAttributes().put("loginUser", userLogin);
                    // 放行
                    log.info("授权成功，当前登录人", username, request.getURI().getPath());
                    Long beginVisitTime = exchange.getAttribute(BEGIN_TIME);
                    log.info("访问接口时长" + (System.currentTimeMillis() - beginVisitTime) + "毫秒");
                    return chain.filter(exchange);
                } else {
                    // 过期，需要重写登录
                    log.info("登录过期，需要重新登录", request.getURI().getPath());
//                    exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
//                    return exchange.getResponse().setComplete();
                    return out(exchange.getResponse(), "登录过期，需要重新登录");
                }

            } else {
                log.info("token不合法", request.getURI().getPath());
//                exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
//                return exchange.getResponse().setComplete();
                return out(exchange.getResponse(), "token不合法");
            }
        } else {
            log.info("授权不合法", request.getURI().getPath());
//            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
//            return exchange.getResponse().setComplete();
            return out(exchange.getResponse(), "授权不合法");
        }

    }


    // 返回体
    public static Mono<Void> out(ServerHttpResponse response, String data) {
        JSONObject message = new JSONObject();
        message.append("success", false);
        message.append("code", 28004);
        message.append("data", data);
        byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }


    /**
     * 这个返回的数值越小，上面的filter优先级就越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

}
